/* C declarations used in actions */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <sys/socket.h>
#include "parse.h"

#define ECHO_PORT 9999
#define BUF_SIZE 4096
#define MAX_URI_LEN 256

char recvbuf[BUF_SIZE];
char sendbuf[BUF_SIZE];

int close_socket(int sock)
{
  if (close(sock))
  {
    fprintf(stderr, "Failed closing socket.\n");
    return 1;
  }
  return 0;
}

void unimplemented(int client_sock)
{
  char buf[1024];

  /* HTTP method 不被支持*/
  sprintf(buf, "HTTP/1.1 501 Not Implemented\r\n\r\n");
  send(client_sock, buf, strlen(buf), 0);
}

void bad_request(int client_sock)
{
  char buf[1024];

  /*回应客户端错误的 HTTP 请求 */
  sprintf(buf, "HTTP/1.1 400 Bad Request\r\n\r\n");
  send(client_sock, buf, sizeof(buf), 0);
}

void not_found(int client_sock)
{
  char buf[1024];

  /* 404 页面 */
  sprintf(buf, "HTTP/1.1 404 Not Found\r\n\r\n");
  send(client_sock, buf, strlen(buf), 0);
}

void version_not_supported(int client_sock)
{
  char buf[1024];

  /* 505 页面 */
  sprintf(buf, "HTTP/1.1 505 HTTP Version not suppoerted\r\n\r\n");
  send(client_sock, buf, strlen(buf), 0);
}

void url_decode(const char *src, char *dest)
{
  const char *p = src;
  char code[3] = {0};
  while (*p && *p != '?')
  {
    if (*p == '%')
    {
      memcpy(code, ++p, 2);
      *dest++ = (char)strtoul(code, NULL, 16);
      p += 2;
    }
    else
    {
      *dest++ = *p++;
    }
  }
  *dest = '\0';
}

void request_get(int client_sock, const char *uri)
{
  struct stat st;
  char path[512];
  char filename[MAX_URI_LEN] = {0};
  const char *cur = uri + 1;
  size_t len = strlen(cur);
  if (len == 0)
  {
    strcpy(filename, "index.html");
  }
  else
  {
    url_decode(cur, filename);
  }
  printf("%s\n", filename);

  /*格式化 url 到 path 数组，html 文件都在 htdocs 中*/
  sprintf(path, "static_site%s", uri);
  /*默认情况为 index.html */
  if (path[strlen(path) - 1] == '/')
    strcat(path, "index.html");
  /*根据路径找到对应文件 */
  if (stat(path, &st) == -1)
  {
    /*回应客户端文件找不到*/
    not_found(client_sock);
  }
  else
  {
    FILE *f = fopen(filename, "r");
    if (NULL == f)
    {
      printf("Open %s failed\n", filename);
      return;
    }
    char header[] =
        "HTTP/1.1 200 OK\r\n"
        "Content-Type: text/html\r\n"
        "\r\n";
    send(client_sock, header, sizeof(header), 0);

    char line[128] = {0};
    while (fgets(line, sizeof(line), f) != NULL)
    {
      send(client_sock, line, strlen(line), 0);
      memset(line, 0, sizeof(line));
    }
    char end[] = "\r\n";
    send(client_sock, end, 2, 0);
    fclose(f);
  }
}

void request_head(int client_sock, const char *uri)
{
  struct stat st;
  char path[512];
  char filename[MAX_URI_LEN] = {0};
  const char *cur = uri + 1;
  size_t len = strlen(cur);
  if (len == 0)
  {
    strcpy(filename, "index.html");
  }
  else
  {
    url_decode(cur, filename);
  }
  printf("%s\n", filename);

  /*格式化 url 到 path 数组，html 文件都在 htdocs 中*/
  sprintf(path, "static_site%s", uri);
  /*默认情况为 index.html */
  if (path[strlen(path) - 1] == '/')
    strcat(path, "index.html");
  /*根据路径找到对应文件 */
  if (stat(path, &st) == -1)
  {
    /*回应客户端文件找不到*/
    not_found(client_sock);
  }
  else
  {
    char header[] =
        "HTTP/1.1 200 OK\r\n"
        "Content-Type: text/html\r\n"
        "\r\n";
    send(client_sock, header, sizeof(header), 0);
  }
}

void request_post(int client_sock, int readret)
{
  strcpy(sendbuf, recvbuf);
  send(client_sock, sendbuf, readret, 0);
}

int main(int argc, char **argv)
{

  int sock, client_sock;
  ssize_t readret;
  socklen_t cli_size;
  struct sockaddr_in addr, cli_addr;

  fprintf(stdout, "----- Liso Server -----\n");

  /* all networked programs must create a socket */
  if ((sock = socket(PF_INET, SOCK_STREAM, 0)) == -1)
  {
    fprintf(stderr, "Failed creating socket.\n");
    return EXIT_FAILURE;
  }

  addr.sin_family = AF_INET;
  addr.sin_port = htons(ECHO_PORT);
  addr.sin_addr.s_addr = INADDR_ANY;

  /* servers bind sockets to ports---notify the OS they accept connections */
  if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)))
  {
    close_socket(sock);
    fprintf(stderr, "Failed binding socket.\n");
    return EXIT_FAILURE;
  }

  if (listen(sock, 5))
  {
    close_socket(sock);
    fprintf(stderr, "Error listening on socket.\n");
    return EXIT_FAILURE;
  }

  /* finally, loop waiting for input and then write it back */
  while (1)
  {
    cli_size = sizeof(cli_addr);
    if ((client_sock = accept(sock, (struct sockaddr *)&cli_addr, &cli_size)) == -1)
    {
      close(sock);
      fprintf(stderr, "Error accepting connection.\n");
      return EXIT_FAILURE;
    }

    readret = 0;
    while ((readret = recv(client_sock, recvbuf, BUF_SIZE, 0)) >= 1)
    {
      // 解析HTTP请求
      printf("readret size %ld\n", readret);
      int index;
      Request *request = parse(recvbuf, readret, client_sock);
      if (request)
      {
        printf("Http Method %s\n", request->http_method);
        printf("Http Version %s\n", request->http_version);
        printf("Http Uri %s\n", request->http_uri);
        for (index = 0; index < request->header_count; index++)
        {
          printf("Request Header\n");
          printf("Header name %s Header Value %s\n", request->headers[index].header_name, request->headers[index].header_value);
        }
        // 如果请求包头部大于8192字节，返回400
        if (sizeof(request) > 8192)
        {
          bad_request(client_sock);
        }
        // 如果HTTP版本不支持，返回505
        if (strcasecmp(request->http_version, "HTTP/1.1") == 0)
        {
          version_not_supported(client_sock);
        }
        // 处理 GET 方法
        if (strcasecmp(request->http_method, "GET") == 0)
        {
          request_get(client_sock, request->http_uri);
        }
        // 处理 HEAD 方法
        else if (strcasecmp(request->http_method, "HEAD") == 0)
        {
          request_head(client_sock, request->http_uri);
        }
        // 处理 POST 方法
        else if (strcasecmp(request->http_method, "POST") == 0)
        {
          request_post(client_sock, readret);
        }
        /*如果不是 GET HEAD POST 则无法处理 返回501*/
        else
        // ((strcasecmp(request->http_method, "GET") && strcasecmp(request->http_method, "HEAD") && strcasecmp(request->http_method, "POST"))
        {
          unimplemented(client_sock);
        }
      }
      else
      {
        printf("Request error\n");
        // 错误请求
        bad_request(client_sock);
      }

      // 释放资源
      free(request);
      memset(sendbuf, 0, BUF_SIZE);
      memset(recvbuf, 0, BUF_SIZE);
    }

    if (readret == -1)
    {
      close_socket(client_sock);
      close_socket(sock);
      fprintf(stderr, "Error reading from client socket.\n");
      return EXIT_FAILURE;
    }

    if (close_socket(client_sock))
    {
      close_socket(sock);
      fprintf(stderr, "Error closing client socket.\n");
      return EXIT_FAILURE;
    }
  }

  close_socket(sock);

  return EXIT_SUCCESS;
}
